import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import Controllers.GlobalData;
import Controllers.GenericToolsForController.RequestMainStatusEnum;
import Controllers.GenericToolsForController.RequestStepStatusEnum;
import Entity.Request;
import Entity.RequestStatusHistory;
import Entity.Status;
import Entity.User;
import Entity.Worker;
import ServerOperation.ReportBuilder;


public class StatisticalTest 
{
	public static List<Request> requestsList;
	public static final long DAY=86400000; // day in MilliSeconds  = 24*60*60*1000.
	public static final double ERRORMARGIN=0.000001; // margin of error in calc

	@BeforeClass
	public static void setUpBeforeClass() throws Exception 
	{
		Request closedreq,openreq;
		requestsList = new ArrayList<Request>();
		
		Worker inspector_62,inspector_2;
		inspector_62 = new Worker(1, 62, "shimon", "Arzuan", "icmbraudesys@gmail.com", "0508-1234567", "shimon1171", "2" );
		inspector_2 = new Worker(2, 2, "guy", "peleg", "icmbraudesys@gmail.com", "0508-1234567", "guyp", "2" );
		User initiator = new User(3, "Ron", "Rosesnfeld","icmbraudesys@gmail.com", "0508-1234567", "roon", "Student");		

		//the current worker that log to the system
		GlobalData.worker = inspector_62;
				
		
		Date start = null;
		Date finish = null;
		Calendar c = Calendar.getInstance();
		c.set(2013, 01, 01);
		
		for (int i=0 ; i<364 ; i++)
		{
			openreq = new Request();
			openreq.setRequestId(i);
			openreq.setInspector(inspector_62);
			openreq.setInitiator(initiator);
			Status openstatus = new Status(RequestStepStatusEnum.REV_REVIEWED_CLOSE, RequestMainStatusEnum.OPEN); 
			
			 c = Calendar.getInstance();
			c.set(2013, 01, 01);
			c.add(Calendar.DAY_OF_MONTH, i);
			start = c.getTime();
			c.add(Calendar.DAY_OF_MONTH, (i%14)+1);
			 finish = c.getTime();
			RequestStatusHistory opencureentStatus = new RequestStatusHistory(1, openstatus, start, finish, finish, null, null);
			openreq.setCurrentStatus(opencureentStatus);
			requestsList.add(openreq);
			
			closedreq = new Request();
			closedreq.setRequestId(i+364);
			closedreq.setInspector(inspector_62);
			closedreq.setInitiator(initiator);
			Status closestatus = new Status(RequestStepStatusEnum.CLS_PASSED, RequestMainStatusEnum.CLOSE); 
			c.add(Calendar.DAY_OF_MONTH, 30);
			finish = c.getTime();
			RequestStatusHistory closedcureentStatus = new RequestStatusHistory(1, closestatus, start, finish, finish, null, null);
			closedreq.setCurrentStatus(closedcureentStatus);
			requestsList.add(closedreq);
		}
		
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void testOpenMedian() 
	{
		double expect = getCalculatedOpenMedian();
		double result = getMedianOpenFromReports();
	    double manuallyCalculated = 7.958333333333333;
	    assertTrue(Math.abs(expect - result)<=ERRORMARGIN);
		assertTrue(Math.abs(manuallyCalculated - result)<=ERRORMARGIN);
		
	}
	
	@Test
	public void testClosedMedian() 
	{
		double expect = getCalculatedClosedMedian();
		double result = getMedianClosedFromReports();
		 double manuallyCalculated = 37.958333333333336;
		   assertTrue(Math.abs(expect - result)<=ERRORMARGIN);
			assertTrue(Math.abs(manuallyCalculated - result)<=ERRORMARGIN);
		
	}
	
	@Test
	public void testOpenDeviation() 
	{
		double expect = getOpenCalculatedDeviation();
		double result = getOpenDeviationFromReports();
		 double manuallyCalculated = 4.030839575545449;
		   assertTrue(Math.abs(expect - result)<=ERRORMARGIN);
			assertTrue(Math.abs(manuallyCalculated - result)<=ERRORMARGIN);
		
	}
	@Test
	public void testCloseDeviation() 
	{
		double expect = getClosedCalculatedDeviation();
		double result = getClosedDeviationFromReports();
		 double manuallyCalculated = 4.030704681513007;
		   assertTrue(Math.abs(expect - result)<=ERRORMARGIN);
			assertTrue(Math.abs(manuallyCalculated - result)<=ERRORMARGIN);
		
	}

	private double getCalculatedOpenMedian() {
		
		List<Double> days = new ArrayList<Double>();
		for (Request req :requestsList)
		{
			if (req.isOpen())
			{
				days.add(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/(double)DAY);
				// System.out.println("calculated:"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime()))/DAY);
			}
			
		}
		Double sortedArray[] = new Double[days.size()];
		sortedArray = days.toArray(sortedArray);
		Arrays.sort(sortedArray);
		double median;
		if (sortedArray.length%2 == 1)
		    median = ((double)sortedArray[(sortedArray.length+1) /2]);
		else
		    median = (double) sortedArray[sortedArray.length/2];
	
      System.out.println("open calculated Median  :"+median);
      return median;
	}
	
	
	private double getMedianOpenFromReports() {
		Map<Integer,Double> tomap = new HashMap<Integer, Double>();
		for (Request req :requestsList)
		{
			if (req.isOpen())
			{
		//	System.out.println("days  :"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/DAY ));
			tomap.put(new Integer(req.getRequestId()), new Double( (((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/DAY )));
			}
		}
		ReportBuilder reportBuilder = new ReportBuilder(null);
		double median = reportBuilder.median(tomap);
		System.out.println("open statistical report Median  :"+median);
		return median;
	}
	
	
private double getCalculatedClosedMedian() {
		
		List<Double> days = new ArrayList<Double>();
		for (Request req :requestsList)
		{
         RequestMainStatusEnum mStatus = req.getCurrentStatus().getStatus().getmStatus();
			 
			 if ( (mStatus != null) && (mStatus == RequestMainStatusEnum.CLOSE ))
			{
				days.add(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/(double)DAY);
		//		 System.out.println("calculated:"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime()))/DAY);
			}
			
		}
		Double sortedArray[] = new Double[days.size()];
		sortedArray = days.toArray(sortedArray);
		Arrays.sort(sortedArray);
		double median;
		if (sortedArray.length%2 == 1)
		    median = ((double)sortedArray[(sortedArray.length+1) /2]);
		else
		    median = (double) sortedArray[sortedArray.length/2];
	
      System.out.println("closed calculated Median  :"+median);
      return median;
	}
	
	
	private double getMedianClosedFromReports() {
		Map<Integer,Double> tomap = new HashMap<Integer, Double>();
		for (Request req :requestsList)
		{
			 RequestMainStatusEnum mStatus = req.getCurrentStatus().getStatus().getmStatus();
			 
			 if ( (mStatus != null) && (mStatus == RequestMainStatusEnum.CLOSE ))
			{
		//	System.out.println("days  :"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/DAY ));
			tomap.put(new Integer(req.getRequestId()), new Double( (((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/DAY )));
			}
		}
		ReportBuilder reportBuilder = new ReportBuilder(null);
		double median = reportBuilder.median(tomap);
		System.out.println("closed statistical report Median  :"+median);
		return median;
	}
	
	
		
		private double getClosedCalculatedDeviation()	
		{
		List<Double> days = new ArrayList<Double>();
		for (Request req :requestsList)
		{
         RequestMainStatusEnum mStatus = req.getCurrentStatus().getStatus().getmStatus();
			 
			 if ( (mStatus != null) && (mStatus == RequestMainStatusEnum.CLOSE ))
			{
				days.add(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/(double)DAY);
		//		 System.out.println("calculated:"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime()))/DAY);
			}
			
		}

			int n = days.size();
			double average = 0;
			for (Double d :days )
			{
				average+=d;
			}
			if (n!=0)
			{
				average = average/n;
			}
				
			double squareSum = 0;
			for (Double d :days )
			{
				squareSum += (d-average)*(d-average);
			}
			
			double deviation = Math.sqrt(squareSum/n);
			System.out.println("closed calculated  deviation  :"+deviation);
			
			return deviation;
	}
		
			private double getOpenDeviationFromReports() {
				Map<Integer,Double> days = new HashMap<Integer,Double>();
				for (Request req :requestsList)
				{     			 
					 if ( req.isOpen())
					{
						days.put(req.getRequestId(),((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/(double)DAY);
				//		 System.out.println("calculated:"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime()))/DAY);
					}
				}
				
				
				ReportBuilder reportBuilder = new ReportBuilder(null);
				double deviation = reportBuilder.deviation(days);
				System.out.println("Open statistical report deviation  :"+deviation);
				return deviation;
		}
			
			private double getClosedDeviationFromReports() {
				Map<Integer,Double> days = new HashMap<Integer,Double>();
				for (Request req :requestsList)
				{     			 
					   RequestMainStatusEnum mStatus = req.getCurrentStatus().getStatus().getmStatus();
						 if ( (mStatus != null) && (mStatus == RequestMainStatusEnum.CLOSE ))
					{
						days.put(req.getRequestId(),((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/(double)DAY);
				//		 System.out.println("calculated:"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime()))/DAY);
					}
				}
				
				
				ReportBuilder reportBuilder = new ReportBuilder(null);
				double deviation = reportBuilder.deviation(days);
				System.out.println("Closed statistical report deviation  :"+deviation);
				return deviation;
		}
		
			private double getOpenCalculatedDeviation()	
			{
			List<Double> days = new ArrayList<Double>();
			for (Request req :requestsList)
			{
				 if ( req.isOpen())
				{
					days.add(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime())/(double)DAY);
			//		 System.out.println("calculated:"+(((double)req.getCurrentStatus().getFinishDate().getTime()-(double)req.getCurrentStatus().getStartDate().getTime()))/DAY);
				}
				
			}

				int n = days.size();
				double average = 0;
				for (Double d :days )
				{
					average+=d;
				}
				if (n!=0)
				{
					average = average/n;
				}
				System.out.println("average in calculated: "+average );
				System.out.println("n in calculated: "+n );
				double squareSum = 0;
				for (Double d :days )
				{
					squareSum += (d-average)*(d-average);
				}
				
				double deviation = Math.sqrt(squareSum/n);
				System.out.println("open calculated  deviation  :"+deviation);
				
				return deviation;
		}
			
			
			
			
	}


